package cn.heyige.backend.service.impl;

import cn.heyige.backend.dto.*;
import cn.hutool.crypto.digest.DigestUtil;
import cn.heyige.backend.entity.UserDO;
import cn.heyige.backend.mapper.UserMapper;
import cn.heyige.backend.service.SimpleAuthService;
import cn.heyige.backend.util.JwtUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * 简化版认证服务实现类
 * 
 * 专注于核心认证功能，提高系统安全性和性能
 * 
 * @author CodeBuddy
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class SimpleAuthServiceImpl implements SimpleAuthService {

    private final UserMapper userMapper;
    private final JwtUtil jwtUtil;
    private final RedisTemplate<String, String> redisTemplate;

    private static final String SMS_CODE_PREFIX = "sms:code:";
    private static final String TOKEN_BLACKLIST_PREFIX = "blacklist:token:";
    private static final String USER_TOKENS_PREFIX = "user:tokens:";
    private static final String RATE_LIMIT_PREFIX = "rate:limit:";
    private static final int ACCESS_TOKEN_EXPIRE_SECONDS = 7200;
    private static final int SMS_CODE_EXPIRE_SECONDS = 300;
    private static final int RATE_LIMIT_EXPIRE_SECONDS = 60;

    @Override
    public SendCodeResponse sendVerificationCode(SendCodeRequest request) {
        try {
            // 参数校验
            if (!StringUtils.hasText(request.getPhone()) || !StringUtils.hasText(request.getType())) {
                throw new RuntimeException("手机号和类型不能为空");
            }

            // 检查发送频率限制
            String rateKey = RATE_LIMIT_PREFIX + request.getPhone();
            if (redisTemplate.hasKey(rateKey)) {
                throw new RuntimeException("发送过于频繁，请稍后再试");
            }

            // 生成6位验证码
            String code = String.format("%06d", new Random().nextInt(999999));

            // 存储验证码到Redis，有效期5分钟
            String codeKey = SMS_CODE_PREFIX + request.getPhone() + ":" + request.getType();
            redisTemplate.opsForValue().set(codeKey, code, SMS_CODE_EXPIRE_SECONDS, TimeUnit.SECONDS);

            // 设置发送频率限制，60秒内不能重复发送
            redisTemplate.opsForValue().set(rateKey, "1", RATE_LIMIT_EXPIRE_SECONDS, TimeUnit.SECONDS);

            // TODO: 调用短信服务发送验证码
            log.info("发送验证码到 {}: {}", maskPhone(request.getPhone()), code);

            return SendCodeResponse.builder()
                    .expiresIn(SMS_CODE_EXPIRE_SECONDS)
                    .interval(RATE_LIMIT_EXPIRE_SECONDS)
                    .codeLength(6)
                    .build();
        } catch (Exception e) {
            log.error("发送验证码失败: {}", e.getMessage());
            throw new RuntimeException("发送验证码失败: " + e.getMessage());
        }
    }

    @Override
    public UserAuthResponse register(UserRegistrationRequestV2 request) {
        try {
            // 参数校验
            if (!StringUtils.hasText(request.getPhone()) || !StringUtils.hasText(request.getPassword())) {
                throw new RuntimeException("手机号和密码不能为空");
            }

            // 验证验证码
            validateVerificationCode(request.getPhone(), "REGISTER", request.getVerificationCode());

            // 检查手机号是否已注册
            UserDO existingUser = userMapper.findByPhone(request.getPhone());
            if (existingUser != null) {
                throw new RuntimeException("手机号已注册");
            }

            // 创建用户
            UserDO user = new UserDO();
            user.setPhone(request.getPhone());
            user.setPassword(DigestUtil.bcrypt(request.getPassword()));
            user.setUsername(request.getNickname() != null ? request.getNickname() : "易学新手");
            user.setCreateTime(LocalDateTime.now());
            user.setUpdateTime(LocalDateTime.now());

            // TODO: 处理邀请码逻辑
            if (StringUtils.hasText(request.getInviteCode())) {
                log.info("处理邀请码: {}", request.getInviteCode());
            }

            userMapper.insert(user);

            // 生成JWT令牌
            String accessToken = jwtUtil.generateAccessToken(
                    user.getId(),
                    user.getUsername()
            );
            String refreshToken = jwtUtil.generateRefreshToken(user.getId(), user.getUsername());

            // 缓存用户令牌
            cacheUserToken(user.getId().toString(), accessToken);

            // 构建响应
            UserAuthResponse.UserInfo userInfo = UserAuthResponse.UserInfo.builder()
                    .userId(user.getId().toString())
                    .phone(maskPhone(user.getPhone()))
                    .nickname(user.getUsername())
                    .avatar("")
                    .registerTime(user.getCreateTime())
                    .build();

            return UserAuthResponse.builder()
                    .userId(user.getId().toString())
                    .accessToken(accessToken)
                    .refreshToken(refreshToken)
                    .expiresIn(ACCESS_TOKEN_EXPIRE_SECONDS)
                    .userInfo(userInfo)
                    .build();

        } catch (Exception e) {
            log.error("用户注册失败: {}", e.getMessage());
            throw new RuntimeException("注册失败: " + e.getMessage());
        }
    }

    @Override
    public UserAuthResponse login(UserLoginRequestV2 request) {
        try {
            // 参数校验
            if (!StringUtils.hasText(request.getPhone())) {
                throw new RuntimeException("手机号不能为空");
            }

            if (!StringUtils.hasText(request.getLoginType())) {
                throw new RuntimeException("登录类型不能为空");
            }

            UserDO user;

            if ("PASSWORD".equals(request.getLoginType())) {
                // 密码登录
                if (!StringUtils.hasText(request.getPassword())) {
                    throw new RuntimeException("密码不能为空");
                }

                user = userMapper.findByPhone(request.getPhone());
                if (user == null) {
                    throw new RuntimeException("用户不存在");
                }

                if (!DigestUtil.bcryptCheck(request.getPassword(), user.getPassword())) {
                    throw new RuntimeException("密码错误");
                }
            } else if ("SMS_CODE".equals(request.getLoginType())) {
                // 验证码登录
                if (!StringUtils.hasText(request.getVerificationCode())) {
                    throw new RuntimeException("验证码不能为空");
                }

                validateVerificationCode(request.getPhone(), "LOGIN", request.getVerificationCode());

                user = userMapper.findByPhone(request.getPhone());
                if (user == null) {
                    throw new RuntimeException("用户不存在");
                }
            } else {
                throw new RuntimeException("不支持的登录类型");
            }

            // 更新最后登录时间和登录次数
            user.setLastLoginTime(LocalDateTime.now());
            user.setUpdateTime(LocalDateTime.now());
            user.setLoginCount(user.getLoginCount() + 1);
            userMapper.updateById(user);

            // 生成JWT令牌
            String accessToken = jwtUtil.generateAccessToken(
                    user.getId(),
                    user.getUsername()
            );
            String refreshToken = jwtUtil.generateRefreshToken(user.getId(), user.getUsername());

            // 缓存用户令牌
            cacheUserToken(user.getId().toString(), accessToken);

            // 构建响应
            UserAuthResponse.UserInfo userInfo = UserAuthResponse.UserInfo.builder()
                    .userId(user.getId().toString())
                    .phone(maskPhone(user.getPhone()))
                    .nickname(user.getUsername())
                    .avatar(user.getAvatar())
                    .membershipType(user.getMembershipType())
                    .membershipExpiry(user.getMembershipExpiry())
                    .registerTime(user.getCreateTime())
                    .lastLoginTime(user.getLastLoginTime())
                    .build();

            return UserAuthResponse.builder()
                    .userId(user.getId().toString())
                    .accessToken(accessToken)
                    .refreshToken(refreshToken)
                    .expiresIn(ACCESS_TOKEN_EXPIRE_SECONDS)
                    .userInfo(userInfo)
                    .build();

        } catch (Exception e) {
            log.error("用户登录失败: {}", e.getMessage());
            throw new RuntimeException("登录失败: " + e.getMessage());
        }
    }

    @Override
    public RefreshTokenResponse refreshToken(String refreshToken) {
        try {
            // 参数校验
            if (!StringUtils.hasText(refreshToken)) {
                throw new RuntimeException("刷新令牌不能为空");
            }

            if (!jwtUtil.validateToken(refreshToken)) {
                throw new RuntimeException("刷新令牌无效");
            }

            Integer userId = jwtUtil.extractUserId(refreshToken);
            UserDO user = userMapper.selectById(userId);
            if (user == null) {
                throw new RuntimeException("用户不存在");
            }

            // 生成新的访问令牌
            String newAccessToken = jwtUtil.generateAccessToken(
                    user.getId(),
                    user.getUsername()
            );

            // 缓存新令牌
            cacheUserToken(user.getId().toString(), newAccessToken);

            return RefreshTokenResponse.builder()
                    .accessToken(newAccessToken)
                    .expiresIn(ACCESS_TOKEN_EXPIRE_SECONDS)
                    .build();

        } catch (Exception e) {
            log.error("刷新令牌失败: {}", e.getMessage());
            throw new RuntimeException("刷新令牌失败: " + e.getMessage());
        }
    }

    @Override
    public void logout(String accessToken, Boolean allDevices) {
        try {
            // 参数校验
            if (!StringUtils.hasText(accessToken)) {
                throw new RuntimeException("访问令牌不能为空");
            }

            if (!validateAccessToken(accessToken)) {
                throw new RuntimeException("访问令牌无效");
            }

            Integer userId = jwtUtil.extractUserId(accessToken);

            if (Boolean.TRUE.equals(allDevices)) {
                // 登出所有设备，清除用户所有令牌
                String userTokensKey = USER_TOKENS_PREFIX + userId;
                redisTemplate.delete(userTokensKey);
                
                // 将所有令牌加入黑名单
                redisTemplate.opsForSet().members(userTokensKey).forEach(token -> {
                    String blacklistKey = TOKEN_BLACKLIST_PREFIX + token;
                    long remainingTime = jwtUtil.getTokenRemainingTime(token);
                    if (remainingTime > 0) {
                        redisTemplate.opsForValue().set(blacklistKey, "1", remainingTime, TimeUnit.SECONDS);
                    }
                });
            } else {
                // 只登出当前设备，将当前令牌加入黑名单
                String blacklistKey = TOKEN_BLACKLIST_PREFIX + accessToken;
                long remainingTime = jwtUtil.getTokenRemainingTime(accessToken);
                if (remainingTime > 0) {
                    redisTemplate.opsForValue().set(blacklistKey, "1", remainingTime, TimeUnit.SECONDS);
                }
                
                // 从用户令牌集合中移除
                String userTokensKey = USER_TOKENS_PREFIX + userId;
                redisTemplate.opsForSet().remove(userTokensKey, accessToken);
            }

        } catch (Exception e) {
            log.error("用户登出失败: {}", e.getMessage());
            throw new RuntimeException("登出失败: " + e.getMessage());
        }
    }

    @Override
    public boolean validateAccessToken(String token) {
        try {
            // 参数校验
            if (!StringUtils.hasText(token)) {
                return false;
            }

            // 检查令牌格式和签名
            if (!jwtUtil.validateToken(token)) {
                return false;
            }

            // 检查是否在黑名单中
            String blacklistKey = TOKEN_BLACKLIST_PREFIX + token;
            return !redisTemplate.hasKey(blacklistKey);
        } catch (Exception e) {
            log.debug("令牌验证失败: {}", e.getMessage());
            return false;
        }
    }

    @Override
    public Integer getUserIdFromToken(String token) {
        try {
            // 参数校验
            if (!StringUtils.hasText(token)) {
                return null;
            }
            
            return jwtUtil.extractUserId(token);
        } catch (Exception e) {
            log.debug("从令牌获取用户ID失败: {}", e.getMessage());
            return null;
        }
    }

    private void validateVerificationCode(String phone, String type, String code) {
        String codeKey = SMS_CODE_PREFIX + phone + ":" + type;
        String storedCode = redisTemplate.opsForValue().get(codeKey);

        if (storedCode == null) {
            throw new RuntimeException("验证码已过期");
        }

        if (!storedCode.equals(code)) {
            throw new RuntimeException("验证码错误");
        }

        // 验证成功后删除验证码
        redisTemplate.delete(codeKey);
    }

    private void cacheUserToken(String userId, String token) {
        try {
            String userTokensKey = USER_TOKENS_PREFIX + userId;
            redisTemplate.opsForSet().add(userTokensKey, token);
            redisTemplate.expire(userTokensKey, 30, TimeUnit.DAYS);
        } catch (Exception e) {
            log.warn("缓存用户令牌失败: {}", e.getMessage());
        }
    }

    private String maskPhone(String phone) {
        if (phone == null || phone.length() != 11) {
            return phone;
        }
        return phone.substring(0, 3) + "****" + phone.substring(7);
    }
}